
Imports System.Threading
Imports std

Public Interface cacheManagerInterface(Of nameType As IComparable(Of nameType), valueType)
    Sub add(ByVal name As nameType, ByVal value As valueType)
    Function [get](ByVal name As nameType, ByRef value As valueType) As Boolean
    Function [get](ByVal name As nameType) As valueType
    Function have(ByVal name As nameType) As Boolean
    Function size() As Int64
    Sub clear()
    Function [erase](ByVal name As nameType) As Boolean
    Function empty() As Boolean
    Function traversal(ByVal d As void(Of nameType, valueType)) As Boolean
    Function cachesize() As Int64
    Function retireticks() As Int64
End Interface

Public Class cacheManagerCounters
    Private Shared _CACHEMANAGER_HIT_PERCENTAGE As Int64

    Public Shared Function CACHEMANAGER_HIT_PERCENTAGE() As Int64
        Return _CACHEMANAGER_HIT_PERCENTAGE
    End Function

    Shared Sub New()
        _CACHEMANAGER_HIT_PERCENTAGE = _
            counter.registerAverageAndLastAverage("CACHEMANAGER_HIT_PERCENTAGE")
    End Sub
End Class

Public Class _cacheManagerS(Of nameType As IComparable(Of nameType), valueType)
    Implements cacheManagerInterface(Of nameType, valueType)
    Public Const notRetireTicks As Int64 = npos
    Public Const defaultCacheSize As Int64 = 1024
    Public Const defaultRetireTicks As Int64 = notRetireTicks
    Public Const defaultThreadsafe As Boolean = False

    Private Sub assert(ByVal v As Boolean, Optional ByVal msg As String = Nothing)
        If Not v Then
            writeConsoleline(ToString())
            _debug.assert(v, msg)
        End If
    End Sub

    Private _map As map(Of nameType, valueType) = Nothing
    Private _mapheap As mapheap(Of nameType, reverseDate) = Nothing
    Private _size As Int64 = defaultCacheSize
    Private _lock As sequentialLock = Nothing
    Private _retireticks As Int64 = defaultRetireTicks

    Private Sub initial(Optional ByVal threadsafe As Boolean = defaultThreadsafe, _
                        Optional ByVal cachesize As Int64 = defaultCacheSize, _
                        Optional ByVal retireticks As Int64 = defaultRetireTicks)
        If isdebugmode() AndAlso Not GetType(valueType).IsValueType AndAlso Not isCloneable(Of valueType)() Then
            raiseError("cache type which is not cloneable " + GetType(valueType).FullName() _
                       + ", may cause data lost or corrupt.", errorHandle.errorType.exclamation)
        End If

        _map = New map(Of nameType, valueType)()
        _mapheap = New mapheap(Of nameType, reverseDate)()
        If threadsafe Then
            _lock = New sequentialLock()
        End If
        _size = cachesize
        _retireticks = retireticks
    End Sub

    Public Sub New(ByVal threadsafe As Boolean, ByVal cachesize As Int64, ByVal retireticks As Int64)
        initial(threadsafe, cachesize, retireticks)
    End Sub

    Public Function retireticks() As Int64 Implements cacheManagerInterface(Of nameType, valueType).retireticks
        Return _retireticks
    End Function

    Public Function lock() As sequentialLock
        Return _lock
    End Function

    Public Function cachesize() As Int64 Implements cacheManagerInterface(Of nameType, valueType).cachesize
        Return _size
    End Function

    Private Sub startmutex()
        If Not lock() Is Nothing Then
            lock().wait()
        End If
    End Sub

    Private Sub endmutex()
        If Not lock() Is Nothing Then
            lock().release()
        End If
    End Sub

    Private Sub addImpl(ByVal name As nameType, ByVal value As valueType)
        _map.insert(name, value)
        _mapheap.insert(name, DateTime.Now)
        If sizeImpl() > cachesize() Then
            Dim oldname As nameType = Nothing
            _mapheap.pop_front(oldname, Nothing)
            assert(Not oldname Is Nothing, "_mapheap.pop_front(oldname, Nothing)) failed")
            assert(_map.erase(oldname), "_map.erase(oldname) returns false. oldname = " + Convert.ToString(oldname))
        End If
    End Sub

    Public Sub add(ByVal name As nameType, ByVal value As valueType) _
        Implements cacheManagerInterface(Of nameType, valueType).add
        If Not name Is Nothing Then
            startmutex()
            addImpl(name, value)
            endmutex()
        End If
    End Sub

    Private Function haveImpl(ByVal name As nameType) As Boolean
        Return _map.find(name) <> _map.end()
    End Function

    Public Function have(ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).have
        If Not name Is Nothing Then
            Dim rtn As Boolean = False
            startmutex()
            rtn = haveImpl(name)
            endmutex()
            Return rtn
        Else
            Return False
        End If
    End Function

    Private Function getImpl(ByVal name As nameType, ByRef value As valueType) As Boolean
        Dim work As map(Of nameType, valueType).iterator = Nothing
        Dim rtn As Boolean = False
        work = _map.find(name)
        If work <> _map.end() Then
            If retireticks() > 0 Then
                Dim it As mapheap(Of nameType, reverseDate).iterator
                it = _mapheap.find(name)
                assert(it <> _mapheap.end, "catch _map / _mapheap not consistency.")
                If (Now.ticks() - (+it).first.data.Ticks) < retireticks() Then
                    copy(value, (+work).second)
                    Return True
                Else
                    eraseImpl(name)
                    Return False
                End If
            Else
                copy(value, (+work).second)
                _mapheap.insert(name, DateTime.Now)
                Return True
            End If
        Else
            Return False
        End If
    End Function

    Public Function [get](ByVal name As nameType, ByRef value As valueType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).get
        If Not name Is Nothing Then
            Dim rtn As Boolean = False
            startmutex()
            rtn = getImpl(name, value)
            endmutex()
            If rtn Then
                counter.increase(cacheManagerCounters.CACHEMANAGER_HIT_PERCENTAGE(), 100)
                Return True
            Else
                counter.increase(cacheManagerCounters.CACHEMANAGER_HIT_PERCENTAGE(), 0)
                Return False
            End If
        Else
            Return False
        End If
    End Function

    Public Function [get](ByVal name As nameType) As valueType _
        Implements cacheManagerInterface(Of nameType, valueType).get
        Dim rtn As valueType = Nothing
        [get](name, rtn)
        Return rtn
    End Function

    Private Sub clearImpl()
        _map.clear()
        _mapheap.clear()
        If isdebugmode() Then
            assert(emptyImpl(), "cannot clear.")
        End If
    End Sub

    Public Sub clear() Implements cacheManagerInterface(Of nameType, valueType).clear
        startmutex()
        clearImpl()
        endmutex()
    End Sub

    Protected Function eraseImpl(ByVal name As nameType) As Boolean
        If _map.erase(name) Then
            assert(_mapheap.erase(name), "_mapheap.erase(name) returns false.")
            Return True
        Else
            Return False
        End If
    End Function

    Public Function [erase](ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).erase
        Dim rtn As Boolean = False
        startmutex()
        rtn = eraseImpl(name)
        endmutex()
        Return rtn
    End Function

    Private Function sizeImpl() As Int64
        If isdebugmode() Then
            assert(_mapheap.size() = _map.size(), "_mapheap.size() <> _map.size()")
        End If
        Return _map.size()
    End Function

    Public Function size() As Int64 Implements cacheManagerInterface(Of nameType, valueType).size
        If isdebugmode() Then
            Dim rtn As Int64
            startmutex()
            rtn = sizeImpl()
            endmutex()
            Return rtn
        Else
            Return sizeImpl()
        End If
    End Function

    Private Function emptyImpl() As Boolean
        If isdebugmode() Then
            assert(_mapheap.empty() = _map.empty(), "_mapheap.empty() <> _map.empty()")
        End If
        Return _map.empty()
    End Function

    Public Function empty() As Boolean Implements cacheManagerInterface(Of nameType, valueType).empty
        If isdebugmode() Then
            Dim rtn As Boolean
            startmutex()
            rtn = emptyImpl()
            endmutex()
            Return rtn
        Else
            Return emptyImpl()
        End If
    End Function

    Private Function traversalImpl(ByVal d As void(Of nameType, valueType)) As Boolean
        Dim it As map(Of nameType, valueType).iterator = Nothing
        it = _map.begin()
        While it <> _map.end()
            d((+it).first(), (+it).second)
            it += 1
        End While
        Return True
    End Function

    Public Function traversal(ByVal d As void(Of nameType, valueType)) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).traversal
        If d Is Nothing Then
            Return False
        Else
            Dim rtn As Boolean = False
            startmutex()
            rtn = traversalImpl(d)
            endmutex()
            Return rtn
        End If
    End Function

    Private Function ToStringImpl() As String
        Return Convert.ToString(_map) + newline.incode() + Convert.ToString(_mapheap)
    End Function

    Public Overrides Function ToString() As String
        Dim rtn As String = Nothing
        startmutex()
        rtn = ToStringImpl()
        endmutex()
        Return rtn
    End Function
End Class

Public Class _cacheManagerM(Of nameType As IComparable(Of nameType), valueType)
    Implements cacheManagerInterface(Of nameType, valueType)
    Public Const defaultSingleCacheCount As Int64 = 127

    Private impls() As _cacheManagerS(Of nameType, valueType) = Nothing

    Public Sub New(ByVal threadsafe As Boolean, ByVal cachesize As Int64, ByVal retireticks As Int64, _
                   Optional ByVal singleCacheCount As Int64 = defaultSingleCacheCount)
        If singleCacheCount > cachesize Then
            singleCacheCount = cachesize
        End If
        If singleCacheCount <= 0 Then
            singleCacheCount = 1
        End If

        Dim onesize As Int64
        onesize = cachesize \ singleCacheCount + ifoper(cachesize Mod singleCacheCount > 0, 1, 0)
        ReDim impls(singleCacheCount - 1)
        For i As Int64 = 0 To singleCacheCount - 1
            impls(i) = New _cacheManagerS(Of nameType, valueType)(threadsafe, onesize, retireticks)
        Next
    End Sub

    Private Function chooseCache(ByVal name As nameType) As Int64
        Return signing(name) Mod impls.Length()
    End Function

    Public Sub add(ByVal name As nameType, ByVal value As valueType) _
        Implements cacheManagerInterface(Of nameType, valueType).add
        impls(chooseCache(name)).add(name, value)
    End Sub

    Public Function [get](ByVal name As nameType, ByRef value As valueType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).get
        Return impls(chooseCache(name)).get(name, value)
    End Function

    Public Function [get](ByVal name As nameType) As valueType _
        Implements cacheManagerInterface(Of nameType, valueType).get
        Return impls(chooseCache(name)).get(name)
    End Function

    Public Function have(ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).have
        Return impls(chooseCache(name)).have(name)
    End Function

    Public Function size() As Int64 Implements cacheManagerInterface(Of nameType, valueType).size
        Dim rtn As Int64 = 0
        For i As Int64 = 0 To impls.Length() - 1
            rtn += impls(i).size()
        Next
        Return rtn
    End Function

    Public Sub clear() Implements cacheManagerInterface(Of nameType, valueType).clear
        For i As Int64 = 0 To impls.Length() - 1
            impls(i).clear()
        Next
    End Sub

    Public Function [erase](ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).erase
        Return impls(chooseCache(name)).erase(name)
    End Function

    Public Function empty() As Boolean Implements cacheManagerInterface(Of nameType, valueType).empty
        For i As Int64 = 0 To impls.Length() - 1
            If Not impls(i).empty() Then
                Return False
            End If
        Next
        Return True
    End Function

    Public Function traversal(ByVal d As void(Of nameType, valueType)) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).traversal
        For i As Int64 = 0 To impls.Length() - 1
            If Not impls(i).traversal(d) Then
                Return False
            End If
        Next

        Return True
    End Function

    Public Function cachesize() As Int64 Implements cacheManagerInterface(Of nameType, valueType).cachesize
        Return impls(0).cachesize() * impls.Length
    End Function

    Public Function retireticks() As Int64 Implements cacheManagerInterface(Of nameType, valueType).retireticks
        Return impls(0).retireticks()
    End Function
End Class

Public Class cacheManager(Of nameType As IComparable(Of nameType), valueType)
    Implements cacheManagerInterface(Of nameType, valueType)

    Public Const defaultSingleCacheCount As Int64 = _cacheManagerM(Of nameType, valueType).defaultSingleCacheCount
    Public Const notRetireTicks As Int64 = _cacheManagerS(Of nameType, valueType).notRetireTicks
    Public Const defaultCacheSize As Int64 = _cacheManagerS(Of nameType, valueType).defaultCacheSize
    Public Const defaultRetireTicks As Int64 = _cacheManagerS(Of nameType, valueType).defaultRetireTicks
    Public Const defaultThreadsafe As Boolean = _cacheManagerS(Of nameType, valueType).defaultThreadsafe

    Private impl As cacheManagerInterface(Of nameType, valueType) = Nothing

    Public Sub New(ByVal threadsafe As Boolean, ByVal cachesize As Int64, ByVal retireticks As Int64, _
                   Optional ByVal singleCacheCount As Int64 = defaultSingleCacheCount)
        If Not threadsafe OrElse singleCacheCount <= 1 Then
            impl = New _cacheManagerS(Of nameType, valueType)(threadsafe, cachesize, retireticks)
        Else
            impl = New _cacheManagerM(Of nameType, valueType)(threadsafe, cachesize, retireticks, singleCacheCount)
        End If
    End Sub

    Public Sub add(ByVal name As nameType, ByVal value As valueType) _
        Implements cacheManagerInterface(Of nameType, valueType).add
        impl.add(name, value)
    End Sub

    Public Function cachesize() As Int64 Implements cacheManagerInterface(Of nameType, valueType).cachesize
        Return impl.cachesize()
    End Function

    Public Sub clear() Implements cacheManagerInterface(Of nameType, valueType).clear
        impl.clear()
    End Sub

    Public Function empty() As Boolean Implements cacheManagerInterface(Of nameType, valueType).empty
        Return impl.empty()
    End Function

    Public Function [erase](ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).erase
        Return impl.erase(name)
    End Function

    Public Function [get](ByVal name As nameType) As valueType _
        Implements cacheManagerInterface(Of nameType, valueType).get
        Return impl.get(name)
    End Function

    Public Function [get](ByVal name As nameType, ByRef value As valueType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).get
        Return impl.get(name, value)
    End Function

    Public Function have(ByVal name As nameType) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).have
        Return impl.have(name)
    End Function

    Public Function traversal(ByVal d As void(Of nameType, valueType)) As Boolean _
        Implements cacheManagerInterface(Of nameType, valueType).traversal
        Return impl.traversal(d)
    End Function

    Public Function retireticks() As Int64 Implements cacheManagerInterface(Of nameType, valueType).retireticks
        Return impl.retireticks()
    End Function

    Public Function size() As Int64 Implements cacheManagerInterface(Of nameType, valueType).size
        Return impl.size()
    End Function

    Public Shared Sub unittest()
        Console.WriteLine("**** cacheManagerUnitTest *****")

        Dim firstRound As Boolean = True
        Dim cache As cacheManager(Of String, Int32) = Nothing
        cache = New cacheManager(Of String, Int32)(cacheManager(Of String, Int32).defaultThreadsafe _
                                                   , 4, cacheManager(Of String, Int32).defaultRetireTicks)
        Dim key As String = Nothing
        Dim value As Double = 0
retry:
        Console.Write("input key string ")
        key = Console.ReadLine
        While Not key Is Nothing
            Dim str As String = Nothing
            Do
                Console.Write("input value double ")
                str = Console.ReadLine
            Loop Until convertor.todouble(str, value)
            cache.add(key, value)
            Console.Write("input key string ")
            key = Console.ReadLine
        End While

        Console.WriteLine("cache.size() = " + Convert.ToString(cache.size()))
        Console.WriteLine("cache.empty() = " + Convert.ToString(cache.empty()))
        Console.WriteLine("output elements.")
        Console.Write("input key string ")
        key = Console.ReadLine
        While Not key Is Nothing
            If cache.get(key, value) Then
                Console.WriteLine(key + character.tab + Convert.ToString(value))
            Else
                Console.WriteLine("not existing " + key + " in cacheManager")
            End If
            Console.Write("input key string ")
            key = Console.ReadLine
        End While

        If firstRound Then
            firstRound = False
            Console.WriteLine("using clear method, and do again.")
            cache.clear()
            GoTo retry
        End If
        Console.WriteLine("********************************")
    End Sub

    Private Class automaticalCacheUnittestInfo
        Public cache As pointer(Of cacheManager(Of nameType, valueType)) = Nothing
        Public keys As pointer(Of vector(Of nameType)) = Nothing
    End Class

    Private Shared Sub automaticalCache(ByVal obj As Object)
        Dim roundcount As Int64 = 1024
        If isreleasebuild() Then
            roundcount *= 20
        End If

        Dim info As cacheManager(Of String, bytes).automaticalCacheUnittestInfo = Nothing
        info = convertor.cast(Of cacheManager(Of String, bytes).automaticalCacheUnittestInfo)(obj)
        Dim cache As cacheManager(Of String, bytes) = Nothing
        Dim keys As vector(Of String) = Nothing
        cache = +(info.cache)
        keys = +(info.keys)

        Dim i As Int64
        For i = 0 To roundcount - 1
            Dim j As Int64
            j = rnd(0, keys.size(), True)
            If rnd(0, 2, True) = 0 Then
                Dim s As String = Nothing
                s = createGUID()
                raiseError("add " + keys(j) + " with data " + s)
                For k As Int64 = 0 To 9
                    s += s
                Next
                cache.add(keys(j), encoding.utf8().GetBytes(s))
            Else
                raiseError("get " + keys(j))
                cache.get(keys(j), Nothing)
            End If
            sleepTicks(rnd(0, millisecondsToTicks(1), True))
        Next
    End Sub

    Public Shared Sub automaticalUnittest()
        Const cachesize As Int64 = 1024
        Const threadcount As Int64 = 16

        raiseError("********** cacheManager Automatical Unittest **********", errorHandle.errorType.application)

        raiseError("prepare keys vector")
        Dim keys As vector(Of String) = Nothing
        keys = New vector(Of String)()
        Dim i As Int64
        For i = 0 To (cachesize << 2) - 1
            keys.push_back(strleft(createGUID(), 5))
        Next

        Dim round As Int64
        For round = 0 To 2
            Dim cache As cacheManager(Of String, bytes) = Nothing
            If round = 0 Then
                raiseError("retire ticks set to 4*1000")
                cache = New cacheManager(Of String, bytes)(True, cachesize, 4 * 1000)
            ElseIf round = 1 Then
                raiseError("retire ticks set to -1")
                cache = New cacheManager(Of String, bytes)(True, cachesize, npos)
            Else
                raiseError("retire ticks set to 1")
                cache = New cacheManager(Of String, bytes)(True, cachesize, 1)
            End If

            Dim info As cacheManager(Of String, bytes).automaticalCacheUnittestInfo = Nothing
            info = New cacheManager(Of String, bytes).automaticalCacheUnittestInfo()
            info.cache = New pointer(Of cacheManager(Of String, bytes))(cache)
            info.keys = New pointer(Of vector(Of String))(keys)

            raiseError("start automaticalCache threads")
            Dim threads(threadcount - 1) As Thread
            For i = 0 To threadcount - 1
                threads(i) = startThread(AddressOf automaticalCache, info)
            Next

            For i = 0 To threadcount - 1
                assert(Not threads(i) Is Nothing, "thread(" + Convert.ToString(i) + ") is nothing.")
                threads(i).Join()
            Next
        Next

        raiseError("******* finish cacheManager Automatical Unittest *******", errorHandle.errorType.application)
    End Sub
End Class